/**
  ******************************************************************************
  * @file   pd_h_flash.h
  * @author Chris
  * @since  2023/11/24
  *
  * @brief  Header file of peripheral driver for FLASH(Hardware-Implemented).
  *
  ******************************************************************************
  */
#ifndef PD_H_FLASH_H
#define PD_H_FLASH_H

#include "stm32g4xx.h"

/**
  * @brief  HAL Status structures definition
  */
typedef enum {
    FLASH_OK = 0x00U,
    FLASH_ERROR = 0x01U,
    FLASH_BUSY = 0x02U,
    FLASH_TIMEOUT = 0x03U
} FlashStatus;

/**
  * @brief  HAL Lock structures definition
  */
typedef enum {
    FLASH_UNLOCKED = 0x00U,
    FLASH_LOCKED = 0x01U
} FlashLockStatus;

/**
  * @brief  FLASH Erase structure definition
  */
typedef struct {
    uint32_t TypeErase;   /*!< Mass erase or page erase.
                             This parameter can be a value of @ref FLASH_Type_Erase */
    uint32_t Banks;       /*!< Select bank to erase.
                             This parameter must be a value of @ref FLASH_Banks
                             (FLASH_BANK_BOTH should be used only for mass erase) */
    uint32_t Page;        /*!< Initial Flash page to erase when page erase is disabled.
                             This parameter must be a value between 0 and (max number of pages in the bank - 1)
                             (eg : 127 for 512KB dual bank) */
    uint32_t NbPages;     /*!< Number of pages to be erased.
                             This parameter must be a value between 1 and (max number of pages in the bank - value of initial page)*/
} LL_FLASH_EraseInitTypeDef;


/**
  * @brief  FLASH Procedure structure definition
  */
typedef enum {
    FLASH_PROC_NONE = 0,
    FLASH_PROC_PAGE_ERASE,
    FLASH_PROC_MASS_ERASE,
    FLASH_PROC_PROGRAM,
    FLASH_PROC_PROGRAM_LAST
} LL_FLASH_ProcedureTypeDef;

/**
  * @brief  FLASH Cache structure definition
  */
typedef enum {
    FLASH_CACHE_DISABLED = 0,
    FLASH_CACHE_ICACHE_ENABLED,
    FLASH_CACHE_DCACHE_ENABLED,
    FLASH_CACHE_ICACHE_DCACHE_ENABLED
} FlashCacheState;

/**
  * @brief  FLASH handle Structure definition
  */
typedef struct {
    FlashLockStatus Lock;              /* FLASH locking object */
    __IO uint32_t ErrorCode;         /* FLASH error code */
    __IO LL_FLASH_ProcedureTypeDef ProcedureOnGoing;  /* Internal variable to indicate which procedure is ongoing or not in IT context */
    __IO uint32_t Address;           /* Internal variable to save address selected for program in IT context */
    __IO uint32_t Bank;              /* Internal variable to save current bank selected during erase in IT context */
    __IO uint32_t Page;              /* Internal variable to define the current page which is erasing in IT context */
    __IO uint32_t NbPagesToErase;    /* Internal variable to save the remaining pages to erase in IT context */
    __IO FlashCacheState CacheToReactivate; /* Internal variable to indicate which caches should be reactivated */
} LL_FLASH_ProcessTypeDef;


/** @defgroup FLASH_Error FLASH Error
  * @{
  */
#define FLASH_ERROR_NONE      0x00000000U
#define FLASH_ERROR_OP        FLASH_FLAG_OPERR
#define FLASH_ERROR_PROG      FLASH_FLAG_PROGERR
#define FLASH_ERROR_WRP       FLASH_FLAG_WRPERR
#define FLASH_ERROR_PGA       FLASH_FLAG_PGAERR
#define FLASH_ERROR_SIZ       FLASH_FLAG_SIZERR
#define FLASH_ERROR_PGS       FLASH_FLAG_PGSERR
#define FLASH_ERROR_MIS       FLASH_FLAG_MISERR
#define FLASH_ERROR_FAST      FLASH_FLAG_FASTERR
#define FLASH_ERROR_RD        FLASH_FLAG_RDERR
#define FLASH_ERROR_OPTV      FLASH_FLAG_OPTVERR
#define FLASH_ERROR_ECCC      FLASH_FLAG_ECCC
#define FLASH_ERROR_ECCD      FLASH_FLAG_ECCD

/** @defgroup FLASH_Type_Erase FLASH Erase Type
  * @{
  */
#define FLASH_TYPEERASE_PAGES     0x00U                    /*!<Pages erase only*/
#define FLASH_TYPEERASE_MASSERASE 0x01U                    /*!<Flash mass erase activation*/

/** @defgroup FLASH_Banks FLASH Banks
  * @{
  */
#define FLASH_BANK_1              0x00000001U              /*!< Bank 1   */
#if defined (FLASH_OPTR_DBANK)
#define FLASH_BANK_2              0x00000002U              /*!< Bank 2   */
#define FLASH_BANK_BOTH           (FLASH_BANK_1 | FLASH_BANK_2) /*!< Bank1 and Bank2  */
#else
#define FLASH_BANK_BOTH           FLASH_BANK_1             /*!< Bank 1   */
#endif


/** @defgroup FLASH_Type_Program FLASH Program Type
  * @{
  */
#define FLASH_TYPEPROGRAM_DOUBLEWORD    0x00U              /*!< Program a double-word (64-bit) at a specified address.*/
#define FLASH_TYPEPROGRAM_FAST          0x01U              /*!< Fast program a 32 row double-word (64-bit) at a specified address.
                                                                And another 32 row double-word (64-bit) will be programmed */
#define FLASH_TYPEPROGRAM_FAST_AND_LAST 0x02U              /*!< Fast program a 32 row double-word (64-bit) at a specified address.
                                                                And this is the last 32 row double-word (64-bit) programmed */


/** @defgroup FLASH_Keys FLASH Keys
  * @{
  */
#define FLASH_KEY1                0x45670123U              /*!< Flash key1 */
#define FLASH_KEY2                0xCDEF89ABU              /*!< Flash key2: used with FLASH_KEY1
                                                                to unlock the FLASH registers access */


/** @defgroup FLASH_Flags FLASH Flags Definition
  * @{
  */
#define FLASH_FLAG_EOP            FLASH_SR_EOP             /*!< FLASH End of operation flag */
#define FLASH_FLAG_OPERR          FLASH_SR_OPERR           /*!< FLASH Operation error flag */
#define FLASH_FLAG_PROGERR        FLASH_SR_PROGERR         /*!< FLASH Programming error flag */
#define FLASH_FLAG_WRPERR         FLASH_SR_WRPERR          /*!< FLASH Write protection error flag */
#define FLASH_FLAG_PGAERR         FLASH_SR_PGAERR          /*!< FLASH Programming alignment error flag */
#define FLASH_FLAG_SIZERR         FLASH_SR_SIZERR          /*!< FLASH Size error flag  */
#define FLASH_FLAG_PGSERR         FLASH_SR_PGSERR          /*!< FLASH Programming sequence error flag */
#define FLASH_FLAG_MISERR         FLASH_SR_MISERR          /*!< FLASH Fast programming data miss error flag */
#define FLASH_FLAG_FASTERR        FLASH_SR_FASTERR         /*!< FLASH Fast programming error flag */
#define FLASH_FLAG_RDERR          FLASH_SR_RDERR           /*!< FLASH PCROP read error flag */
#define FLASH_FLAG_OPTVERR        FLASH_SR_OPTVERR         /*!< FLASH Option validity error flag  */
#define FLASH_FLAG_BSY            FLASH_SR_BSY             /*!< FLASH Busy flag */
#define FLASH_FLAG_ECCC           FLASH_ECCR_ECCC          /*!< FLASH ECC correction in 64 LSB bits */
#define FLASH_FLAG_ECCD           FLASH_ECCR_ECCD          /*!< FLASH ECC detection in 64 LSB bits */

#define FLASH_FLAG_SR_ERRORS      (FLASH_FLAG_OPERR   | FLASH_FLAG_PROGERR | FLASH_FLAG_WRPERR | \
                                   FLASH_FLAG_PGAERR  | FLASH_FLAG_SIZERR  | FLASH_FLAG_PGSERR | \
                                   FLASH_FLAG_MISERR  | FLASH_FLAG_FASTERR | FLASH_FLAG_RDERR  | \
                                   FLASH_FLAG_OPTVERR)

#define FLASH_FLAG_ECCR_ERRORS    (FLASH_FLAG_ECCC    | FLASH_FLAG_ECCD)

#define FLASH_FLAG_ALL_ERRORS     (FLASH_FLAG_SR_ERRORS | FLASH_FLAG_ECCR_ERRORS)


#define LL_FLASH_INSTRUCTION_CACHE_ENABLE()  SET_BIT(FLASH->ACR, FLASH_ACR_ICEN)
#define LL_FLASH_INSTRUCTION_CACHE_DISABLE() CLEAR_BIT(FLASH->ACR, FLASH_ACR_ICEN)
#define LL_FLASH_DATA_CACHE_ENABLE()         SET_BIT(FLASH->ACR, FLASH_ACR_DCEN)
#define LL_FLASH_DATA_CACHE_DISABLE()        CLEAR_BIT(FLASH->ACR, FLASH_ACR_DCEN)
#define LL_FLASH_INSTRUCTION_CACHE_RESET()   do { SET_BIT(FLASH->ACR, FLASH_ACR_ICRST);   \
                                                     CLEAR_BIT(FLASH->ACR, FLASH_ACR_ICRST); \
                                                   } while (0)
#define LL_FLASH_DATA_CACHE_RESET()          do { SET_BIT(FLASH->ACR, FLASH_ACR_DCRST);   \
                                                     CLEAR_BIT(FLASH->ACR, FLASH_ACR_DCRST); \
                                                   } while (0)

/**
  * @brief  Check whether the specified FLASH flag is set or not.
  * @param  __FLAG__ specifies the FLASH flag to check.
  *   This parameter can be one of the following values:
  *     @arg FLASH_FLAG_EOP: FLASH End of Operation flag
  *     @arg FLASH_FLAG_OPERR: FLASH Operation error flag
  *     @arg FLASH_FLAG_PROGERR: FLASH Programming error flag
  *     @arg FLASH_FLAG_WRPERR: FLASH Write protection error flag
  *     @arg FLASH_FLAG_PGAERR: FLASH Programming alignment error flag
  *     @arg FLASH_FLAG_SIZERR: FLASH Size error flag
  *     @arg FLASH_FLAG_PGSERR: FLASH Programming sequence error flag
  *     @arg FLASH_FLAG_MISERR: FLASH Fast programming data miss error flag
  *     @arg FLASH_FLAG_FASTERR: FLASH Fast programming error flag
  *     @arg FLASH_FLAG_RDERR: FLASH PCROP read  error flag
  *     @arg FLASH_FLAG_OPTVERR: FLASH Option validity error flag
  *     @arg FLASH_FLAG_BSY: FLASH write/erase operations in progress flag
  *     @arg FLASH_FLAG_ECCC: FLASH one ECC error has been detected and corrected in 64 LSB bits
  *     @arg FLASH_FLAG_ECCD: FLASH two ECC errors have been detected in 64 LSB bits
  *     @arg FLASH_FLAG_ECCC2(*): FLASH one ECC error has been detected and corrected in 64 MSB bits (mode 128 bits only)
  *     @arg FLASH_FLAG_ECCD2(*): FLASH two ECC errors have been detected in 64 MSB bits (mode 128 bits only)
  * @note  (*) availability depends on devices
  * @retval The new state of FLASH_FLAG (SET or RESET).
  */
#define LL_FLASH_GET_FLAG(__FLAG__)          ((((__FLAG__) & FLASH_FLAG_ECCR_ERRORS) != 0U) ? \
                                                 (READ_BIT(FLASH->ECCR, (__FLAG__)) == (__FLAG__)) : \
                                                 (READ_BIT(FLASH->SR,   (__FLAG__)) == (__FLAG__)))

/**
  * @brief  Clear the FLASH's pending flags.
  * @param  __FLAG__ specifies the FLASH flags to clear.
  *   This parameter can be any combination of the following values:
  *     @arg FLASH_FLAG_EOP: FLASH End of Operation flag
  *     @arg FLASH_FLAG_OPERR: FLASH Operation error flag
  *     @arg FLASH_FLAG_PROGERR: FLASH Programming error flag
  *     @arg FLASH_FLAG_WRPERR: FLASH Write protection error flag
  *     @arg FLASH_FLAG_PGAERR: FLASH Programming alignment error flag
  *     @arg FLASH_FLAG_SIZERR: FLASH Size error flag
  *     @arg FLASH_FLAG_PGSERR: FLASH Programming sequence error flag
  *     @arg FLASH_FLAG_MISERR: FLASH Fast programming data miss error flag
  *     @arg FLASH_FLAG_FASTERR: FLASH Fast programming error flag
  *     @arg FLASH_FLAG_RDERR: FLASH PCROP read  error flag
  *     @arg FLASH_FLAG_OPTVERR: FLASH Option validity error flag
  *     @arg FLASH_FLAG_ECCC: FLASH one ECC error has been detected and corrected in 64 LSB bits
  *     @arg FLASH_FLAG_ECCD: FLASH two ECC errors have been detected in 64 LSB bits
  *     @arg FLASH_FLAG_ECCC2(*): FLASH one ECC error has been detected and corrected in 64 MSB bits (mode 128 bits only)
  *     @arg FLASH_FLAG_ECCD2(*): FLASH two ECC errors have been detected in 64 MSB bits (mode 128 bits only)
  *     @arg FLASH_FLAG_SR_ERRORS: FLASH All SR errors flags
  *     @arg FLASH_FLAG_ECCR_ERRORS: FLASH All ECCR errors flags
  * @note  (*) availability depends on devices
  * @retval None
  */
#define LL_FLASH_CLEAR_FLAG(__FLAG__)        do { if(((__FLAG__) & FLASH_FLAG_ECCR_ERRORS) != 0U) { SET_BIT(FLASH->ECCR, ((__FLAG__) & FLASH_FLAG_ECCR_ERRORS)); }\
                                                     if(((__FLAG__) & ~(FLASH_FLAG_ECCR_ERRORS)) != 0U) { WRITE_REG(FLASH->SR, ((__FLAG__) & ~(FLASH_FLAG_ECCR_ERRORS))); }\
                                                   } while (0)


#define FLASH_SIZE_DATA_REGISTER        FLASHSIZE_BASE
#define FLASH_SIZE                      ((((*((uint16_t *)FLASH_SIZE_DATA_REGISTER)) == 0xFFFFU)) ? (0x80UL << 10U) : \
                                        (((*((uint32_t *)FLASH_SIZE_DATA_REGISTER)) & 0xFFFFUL) << 10U))
#define FLASH_BANK_SIZE                 (FLASH_SIZE)
#define FLASH_PAGE_NB                   ((FLASH_SIZE == 0x00080000U) ? 256U : \
                                        ((FLASH_SIZE == 0x00040000U) ? 128U : 64U))

#define FLASH_PAGE_SIZE                 0x800U  /* 2 KB */

#define FLASH_TIMEOUT_VALUE             1000U   /* 1 s  */


extern LL_FLASH_ProcessTypeDef pFlash;

uint32_t PD_H_FLASH_ReadWord(uint32_t address);

uint32_t PD_H_FLASH_ReadHalfWord(uint32_t address);

uint32_t PD_H_FLASH_ReadByte(uint32_t address);


FlashStatus PD_H_FLASH_Unlock(void);

FlashStatus PD_H_FLASH_Erase(LL_FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError);

FlashStatus PD_H_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);

FlashStatus PD_H_FLASH_Lock(void);


#endif //PD_H_FLASH_H
